from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

_LOCATOR_MAP = {
    'xpath': By.XPATH,
    'id': By.ID,
    'tag_name': By.TAG_NAME,
    'name': By.NAME,
    'css': By.CSS_SELECTOR,
    'class1': By.CLASS_NAME
}


class PageObject(object):
    """Page Object pattern.

    :param webdriver: `selenium.webdriver.WebDriver`
        Selenium webdriver instance
    :param root_uri: `str`
        Root URI to base any calls to the ``PageObject.get`` method. If not defined
        in the constructor it will try and look it from the webdriver object.
    """

    def __init__(self, webdriver: object, root_uri: object = None) -> object:
        self.w = webdriver
        self.root_uri = root_uri if root_uri else getattr(self.w, 'root_uri', None)

    def get(self, uri):
        """
        :param uri:  URI to GET, based off of the root_uri attribute.
        """
        root_uri = self.root_uri or ''
        self.w.get(root_uri + uri)




class PageElement(object):

    def __init__(self, context=False, **kwargs):
        """处理传进来的元素定位键值对，让(id='kw')变成（By.id, 'kw'）"""
        if not kwargs:
            raise ValueError("Please specify a locator")
        if len(kwargs) > 1:
            raise ValueError("Please specify only one locator")
        k, v = next(iter(kwargs.items()))
        self.locator = (_LOCATOR_MAP[k], v)
        self.has_context = bool(context)


    def find(self, context):
        try:
            #优化等待元素可交互
            # ele = WebDriverWait(context, 3, 1).until(lambda x: x.find_elements(*self.locator))
            element = WebDriverWait(context, 10).until(
                EC.element_to_be_clickable(self.locator)
            )
        except NoSuchElementException:
            return None
        except TimeoutException:
            return None
        else:
            return element




    def __get__(self, instance, owner, context=None):
        """实现元素定位find_element()"""
        if not instance:
            return None

        if not context and self.has_context:
            return lambda ctx: self.__get__(instance, owner, context=ctx)

        if not context:
            context = instance.w

        return self.find(context)


    def __set__(self, instance, value):
        if self.has_context:
            raise ValueError("Sorry, the set descriptor doesn't support elements with context.")

        elems = self.__get__(instance, instance.__class__)
        if not elems:
            raise ValueError("Can't set value, no elements found")

        if isinstance(elems, list):
            for elem in elems:
                elem.send_keys(value)
        else:
            elems.send_keys(value)


# Backwards compatibility with previous versions that used factory methods
page_elements = PageElement

